package com.asa.tongji.huiguifenxi;

import java.awt.List;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;

import com.asa.hutils.MathHUtils;
import com.asa.numanaly.F;
import com.asa.tongji.FenBuFunction;
import com.asa.tongji.TUtils1;
import com.asa.tongji.TUtils2;
import com.asa.tongji.TUtils3;
import com.asa.tongji.bean.HuiGuiFenXiBaoGao;
import com.asa.utils.MathUtils;

/**
 * 回归分析，但是由于F分布处理得不是很好，很多检验，无法达到要求
 * 
 * @author Administrator
 *
 */
public class HuiGui {

	

	/**
	 * 不加精度判断
	 * 
	 * @throws Exception
	 */
	public static HuiGuiFenXiBaoGao huiguifenxinormal(double[] x, double[] y) throws Exception {
		double correlationCoefficient = TUtils1.correlationCoefficient(x, y);

		//System.out.println("相关系数:" + correlationCoefficient);

		double averagex = TUtils1.average(x);
		double averagey = TUtils1.average(y);

		double variancex = TUtils1.variance(x);
		double variancey = TUtils1.variance(y);

		// 协方差
		double sxx = variancex * x.length;
		double syy = variancey * y.length;
		double sxy = TUtils1.covariance(x, y) * x.length;

		double a = sxy / sxx;
		double b = averagey - averagex * a;
		//System.out.println("y=" + a + "*x" + b);
		double[] y_ = new double[y.length];

		for (int i = 0; i < y_.length; i++) {
			y_[i] = a * x[i] + b;
		}

		double sy_y_ = TUtils1.variance(y_) * y.length;
		double syy_ = TUtils1.covariance(y_, y) * y.length;
		double r = syy_ / (Math.sqrt(syy * sy_y_));
		//System.out.println("重相关系数：" + r);
		double rr = r * r;
		//System.out.println("判断系数：" + rr);// 精度越高，结果越接近1
		//System.out.println("判断系数好像也可以这么算" + a * sxy / syy);

		/** 回归系数检验 **/
		double se = (1 - rr) * syy;

		//System.out.println(se);

		double o = Math.sqrt(se / (y.length - 2));

		//System.out.println(o);

		// 这个f求解不出来，原有的函数集里面没有,而且拟合函数有问题，一直得不到解决
		 double jianyantongjiliang = a*a*sxx/(o*o);
		// //System.out.println(jianyantongjiliang);
		 double oo = Math.sqrt(variancey);
		double[][] xtoy = xtoy(x, a, b, Math.sqrt(variancey), y.length);
//		//System.out.println("求特定的置信区间: " + Arrays.toString(xtoy));
		
		// double[] ffengbu = FenBuFunction.ffengbu(0.1, 12, 1);
		// //System.out.println(Arrays.toString(ffengbu));

		double t = FenBuFunction.tfengbu(0.95, y.length - 2);

		//System.out.println(jianyantongjiliang + "    jianyantongjiliang        " + t);
		double[] biaozhuncancha = biaozhuncancha(a, b, se, x, y);
		// //System.out.println(se);
		//
		// double[] biaozhuncancha = biaozhuncancha(a, b, se, x, y);
		// for (int i = 0; i < biaozhuncancha.length; i++) {
		// //System.out.println(biaozhuncancha[i]);
		// }
		HuiGuiFenXiBaoGao baoGao = new HuiGuiFenXiBaoGao();
		baoGao.setA(a);
		baoGao.setB(b);
		baoGao.setCorrelationCoefficient(correlationCoefficient);
		baoGao.setRr(rr);
		baoGao.setZhixindu(0.95);
		baoGao.setDuiyujieguozhixindujianyantongguo(jianyantongjiliang>t);
		baoGao.setYangbenqujianzhi(xtoy);
		baoGao.setBiaozhuncancha(biaozhuncancha);
		baoGao.setOo(oo);
		return baoGao;
	}

	/**
	 * 增加加精度判断，得出更准确的拟合结果，但是会对原数据进行剔除修改
	 * 
	 * @throws Exception
	 */
	public static HuiGuiFenXiBaoGao huiguifenxidetail(double[] x, double[] y) throws Exception {
		double correlationCoefficient;
		double a;
		double b;
		double rr;
		double sxx;
		double o;
		double variancey;
		double[] biaozhuncancha;
		while (true) {
			correlationCoefficient = TUtils1.correlationCoefficient(x, y);
			//System.out.println("相关系数:" + correlationCoefficient);
			double averagex = TUtils1.average(x);
			double averagey = TUtils1.average(y);

			double variancex = TUtils1.variance(x);
			variancey = TUtils1.variance(y);

			// 协方差
			sxx = variancex * x.length;
			double syy = variancey * y.length;
			double sxy = TUtils1.covariance(x, y) * x.length;

			a = sxy / sxx;
			b = averagey - averagex * a;
			//System.out.println("a:" + a);
			//System.out.println("b:" + b);

			//System.out.println("y=" + a + "*x" + b);

			double[] y_ = new double[y.length];

			for (int i = 0; i < y_.length; i++) {
				y_[i] = a * x[i] + b;
			}

			double sy_y_ = TUtils1.variance(y_) * y.length;
			double syy_ = TUtils1.covariance(y_, y) * y.length;
			double r = syy_ / (Math.sqrt(syy * sy_y_));
			//System.out.println("重相关系数：" + r);
			rr = r * r;
			//System.out.println("判断系数：" + rr);// 精度越高，结果越接近1

			//System.out.println("判断系数好像也可以这么算" + a * sxy / syy);

			/** 回归系数检验 **/
			double se = (1 - rr) * syy;

			//System.out.println(se);

			o = Math.sqrt(se / (y.length - 2));

			//System.out.println(o);

			
			//System.out.println(se);

			biaozhuncancha = biaozhuncancha(a, b, se, x, y);
			for (int i = 0; i < biaozhuncancha.length; i++) {
				//System.out.println(biaozhuncancha[i]);
			}

			
			//将离群点的数据剔除
			boolean asa = true;
			ArrayList<Double> listx = new ArrayList<Double>();
			ArrayList<Double> listy = new ArrayList<Double>();
			for (int i = 0; i < biaozhuncancha.length; i++) {
				if (Math.abs(biaozhuncancha[i]) > 3) {
					asa = false;
				} else {
					listx.add(x[i]);
					listy.add(y[i]);
				}
			}

			if (asa || listx.size() < 2) {
				break;
			} else {
				x = new double[listx.size()];
				y = new double[listx.size()];
				for (int i = 0; i < listx.size(); i++) {
					x[i] = listx.get(i);
					y[i] = listy.get(i);
				}
			}

		}

		
		// 这个f求解不出来，原有的函数集里面没有,而且拟合函数有问题，一直得不到解决，我改用t函数吧
		double jianyantongjiliang = a * a * sxx / (o * o);
		double t = FenBuFunction.tfengbu(0.95, y.length - 2);
		//System.out.println(jianyantongjiliang + "    jianyantongjiliang        " + t);
		double oo = Math.sqrt(variancey);
		double[][] xtoy = xtoy(x, a, b, oo, y.length);
		
		
		
		
		
		HuiGuiFenXiBaoGao baoGao = new HuiGuiFenXiBaoGao();
		baoGao.setA(a);
		baoGao.setB(b);
		baoGao.setCorrelationCoefficient(correlationCoefficient);
		baoGao.setRr(rr);
		baoGao.setZhixindu(0.95);
		baoGao.setDuiyujieguozhixindujianyantongguo(jianyantongjiliang>t);
		baoGao.setYangbenqujianzhi(xtoy);
		baoGao.setBiaozhuncancha(biaozhuncancha);
		baoGao.setOo(oo);
		return baoGao;

	}

	/**
	 * 标准化残差
	 */
	public static double[] biaozhuncancha(double a, double b, double se, double[] x, double[] y) {
		double[] y__ = new double[y.length];
		for (int i = 0; i < y.length; i++) {
			y__[i] = (y[i] - (a * x[i] + b)) / (Math.sqrt(se / y.length - 2));
		}
		return y__;
	}

	/**
	 * one x to y[,] 求特定的置信区间
	 * 
	 * @param x
	 *            自变量
	 * @param a
	 *            回归函数斜率
	 * @param b
	 *            回归函数b
	 * @param oo
	 *            总体样本的标准差
	 * @param n
	 *            样本数量
	 * @return
	 */
	public static double[][] xtoy(double[] x, double a, double b, double oo, int n) {
		// 具体某一x确定，y的置信区间
		double[][] zz = new double[x.length][2];
		for (int i = 0; i < x.length; i++) {
			double uy = yuce(x[i], a, b);
			zz[i] = TUtils2.zongtijunzhi(n, oo, uy, 0.9);// 置信区间，没有用f的使用正态的
		}
		return zz;
	}
	public static double[] xtoy(double x, double a, double b, double oo, int n,double aa) {
		// 具体某一x确定，y的置信区间
		double[] zz = new double[2];

		double uy = yuce(x, a, b);
		zz = TUtils2.zongtijunzhi(n, oo, uy, aa);// 置信区间，没有用f的使用正态的
		
		return zz;
	}
	
	
	
	public static double yuce(double x, double a, double b) {
		// 具体某一x确定，y的置信区间
		double uy = a * x + b;
		return uy;
	}
	
	
	
	
	

	
	/**
	 * 
	 *  * 最小二乘法 这个是专门针对线性函数的，在calculate_math中没能做到这个 关键是思路都不一样，所以我们还是在这里重新实现一下算了
	 * 
	 * 
	 * 	矩阵里面的一个思路
	 * 
	 * 	Ax=B	是无解的
	(AT)Ax = (AT)B就是有解的，这是最小二乘法的原理
	
		(未测试)		已经通过测试了，不过矩阵的传参有其普遍性的搞不清矩阵和它的转置的问题，使用时注意看测试用例吧

	 * 值得注意的一点是	A必须是可逆的，更明确的说是里面的所有元素是线性无关的
	 * 
	 * @param A	没一条直线组合的集合 y= a + bx	所有的a的参数和b的参数(a1,b1)(a2,b2)...所组成的集合，简化的写就是（1，x1）（1，x2）...（1，xn）
	 * @param B	y点的值的集合 
	 * @return 应该是个数组， [0]是a，[1]是b	y= a + bx	简化的写就是（y1,y2,y2...yn）
	 */
	public static double[] zuixiaoercheng(double[][] A,double[] B) {
		double[][] AT = MathHUtils.zhuanzhi(A);
		double[][] ATA = MathHUtils.chenfa(A,AT);
//		for (int i = 0; i < ATA.length; i++) {
//			for (int j = 0; j < ATA[i].length; j++) {
//				System.out.print(ATA[i][j]+ "  ");
//			}
//			System.out.println();
//		}
		double[] ATB = new double[A.length];
			for (int j = 0; j < A.length; j++) {
				double[] asa = A[j];
				double one =0;
				for (int k = 0; k < asa.length; k++) {
					double d = asa[k];
					one += d*B[k];
					
				}
				ATB[j] = one;
//				System.out.println(one);
			}	
		
		double[] duoyuanyici = F.fangchengzu1(ATA, ATB);
		
		return duoyuanyici;
	}
	
	
	
	public static void main(String[] args) throws Exception {
//		test1();
		
		
		// y = a + bx
		//这里的例子	1 = a + 1*b;	2 = a + 2*b;	2 = a + 3*b
		//意思是在平面上有3个点，(1,1);(2,2);(3,2)
		//我们要找一条直线函数来通过到点的距离最小
		double[][] A = {{1,1,1},
				{1,2,3}};
		double[] B = {1,2,2};
		
		double[] zuixiaoercheng = zuixiaoercheng(A, B);
		for (int i = 0; i < zuixiaoercheng.length; i++) {
			System.out.println(zuixiaoercheng[i]);
		}
		
		
	}

	private static void test1() throws Exception {
		double[] x;
		double[] y;
		x = new double[15];
		y = new double[15];

		x[0] = 29;
		x[1] = 28;
		x[2] = 34;
		x[3] = 31;
		x[4] = 25;
		x[5] = 29;
		x[6] = 32;
		x[7] = 31;
		x[8] = 24;
		x[9] = 33;
		x[10] = 25;
		x[11] = 31;
		x[12] = 26;
		x[13] = 30;
		x[14] = 30;
		
		
		y[0] = 77;
		y[1] = 62;
		y[2] = 93;
		y[3] = 84;
		y[4] = 59;
		y[5] = 64;
		y[6] = 80;
		y[7] = 75;
		y[8] = 58;
		y[9] = 91;
		y[10] = 51;
		y[11] = 73;
		y[12] = 65;
		y[13] = 84;
		y[14] = 1000;
		huiguifenxinormal(x, y);
		HuiGuiFenXiBaoGao huiguifenxidetail = huiguifenxidetail(x, y);
		//System.out.println(huiguifenxidetail);
		
		double[] xtoy = xtoy(1, 2, 3, 1, 10,0.95);
		//System.out.println(Arrays.toString(xtoy));
	}
	
	
	
	
	

}
